Trends in computer design and associated compiler technology have increased the importance of optimization in program translation. In addition, it is anticipated that, with future computer architectures, optimization will play an increasingly important role; in some cases, an unoptimized translation may not even be available. Thus, there is a growing need to be able to debug optimized code.
Computer programmers typically create computer programs by writing the programs in a form that can not be directly executed by the computer on which the program is intended to operate (the target machine). This form of the program, known as source code, is then translated into instructions that the target machine can directly execute (known as object code or machine instructions). While there are various stages and types of such translation and types of tools for such translation (such as, compilers, assemblers, linkers), for the purpose of the present application, these tools are all subsumed within the term "compiler".
Software development typically involves an iterative process in which a programmer repeatedly goes through the process of making changes in source code and observing the program's behavior as it is executed. To observe the program's behavior, a programmer generally uses a tool or collection of tools known as a debugger.
A programmer often uses a debugger in the following cycle: (1) stop program execution at some potentially useful place; (2) observe program data; (3) formulate hypotheses about the program's behavior; (4) predict the next useful place to stop, and arrange for the program to stop at that place; (5) continue program execution; and (6) repeat these steps as needed.
To support this activity, debuggers typically provide a range of facilities for controlling execution of the program being "debugged" and for observing program data and other state information in the target machine that is executing the program being debugged.
For example, debuggers typically permit a user of the debugger to step program execution one machine instruction at a time or one source statement at a time. Also, debuggers typically provide a breakpoint facility, whereby a user can specify one or more points in the program at which the debugger will stop program execution when any of these points is reached.
Some debuggers provide facilities for stopping program execution based on changes in program variables. For example, a user of the VS debugger from Wang Laboratories can tell the debugger to stop at the next statement that modifies a specified variable. Other debuggers provide a "watch variable" capability: after every instruction or statement, the debugger tests the specified variable, and stops execution when the variable has been changed. Other systems use hardware support to detect when specified memory locations are accessed, thereby permitting breakpoints to be set based on access to program data.
Debuggers often present a visual display of the source code containing the target program's current point of execution, with the portion of source code (line, statement, expression, or some other source construct) that is about to execute being visually designated in some way. Such source-level debuggers generally also permit a user to specify locations in the machine code by referring to the source code.
With straightforward compilation, each source statement is translated into a number of contiguous machine instructions; this results in a one-to-one correspondence between a source statement and a group of machine instructions. By exploiting this simple source code/machine instruction relationship, a debugger can present to the programmer the illusion that the target machine is executing the source code. For example, a request by a programmer to set a breakpoint at a particular source statement has a well-defined interpretation in terms of where in the machine instructions to set the breakpoint. Further, in this situation, a programmer can assume that all machine instructions derived from one source statement all execute before any instructions derived from another source statement, and that the machine instructions execute in the same order that the source statements are intended to execute under any set of inputs.
In order to enhance the quality (speed and/or size) of the machine code generated by compilers, compilers have been enhanced such that the machine code that they generate no longer bears a simple relationship to the source code, such as the following: several noncontiguous machine instructions may be derived from a single source statement; a single machine instruction may correspond to several noncontiguous source statements; some source statements may be eliminated completely. Such compilers are typically known as optimizing compilers.
Because of the lack of a one-to-one correspondence between source statements and groups of machine instructions, source level debugging of optimized machine code is more complex. For example, it becomes more difficult for the debugger to accurately indicate the source code equivalent of the current execution point, and it becomes more difficult for a programmer to correctly specify the location of breakpoints.
Because an optimized program's instructions are not necessarily executed in the order that they appear in the source code listing, statements that precede the current execution point in the source code may not have been executed yet. This makes it risky for the programmer to draw conclusions based on observation of the data on which those statements operate.
In some cases, programmers deal with these problems by disabling the compiler's optimization features, and debugging code that has not be optimized. In other cases, programmers resort to debugging the assembly language form of the program (a text counterpart to the numeric values of machine instructions), with little or no debugger support for finding the related source statements.
There is no one "solution" to the challenges of debugging optimized code. Rather, programmers deal with this challenge by using a variety of techniques that help bridge the gap between the programmer's source code and the machine code that is actually executed by a computer.
Debugging of optimized code has been discussed in many references, including: P. T. Zellweger, "Interactive source-level debugging of optimized programs", Xerox Corporation, Palo Alto Research Center, CSL-84-5, May 1984 (extensive discussion of problems inherent in debugging of optimized code; cites many references; also, see pp. 72-76); M. Copperman, "Debugging optimized code without being mislead", University of California at Santa Cruz, UCSC-CRL-93-21, Jun. 11, 1993 (includes a literature survey at pp. 16-22); U.S. Pat. No. 4,953,084, Meloy, et al., "Method and apparatus using variable ranges to support symbolic debugging of optimized code"; U.S. Pat. No. 5,371,747, Brooks, et al., "Debugger program which includes correlation of computer program source code with optimized object code" (cites many non-patent references).